#include "_public.h"

CLogFile logfile;

int main(int argc, char *argv[]){
    // help
    if(argc != 2){
        printf("\n");
        printf("Using:./checkproc logfilename\n");

        printf("Example:/project/tools1/bin/procctl 10 /project/tools1/bin/checkproc /tmp/log/checkproc.log\n\n");

        printf("本程序用于检查后台服务程序是否超时，如果已超时，就终止它。\n");
        printf("注意：\n");
        printf("  1本程序由procctl,启动运行周期建议为10秒。\n");
        printf("  2为了避免被普通用户误杀,本程序应该用root用户启动。\n");
        printf("  3如果要停止本程序,只能用killall -9 终止。\n\n\n");

        return 0;
    }

    // 关闭全部的信号
    CloseIOAndSignal(true);

    // 打开日志文件
    if(logfile.Open(argv[1],"a+") == false){
        cout << "logfile.Open(" << argv[1] << ") failed." << endl;
        return -1;
    }

    int shmid = 0;
    // 创建/获取共享内存
    if((shmid = shmget((key_t)SHMKEYP, MAXNUMP*sizeof(struct st_procinfo), 0666|IPC_CREAT)) == -1){
        logfile.Write("create/get shared memory(%x) failed.\n", SHMKEYP);
        return false;
    }

    // 将共享内存连接到当前进程的地址空间
    struct st_procinfo *shm = (struct st_procinfo *)shmat(shmid, 0, 0);

    // 遍历共享内存中全部的记录（不遍历无法知道哪个记录为空，哪个有心跳信息）
    for(int i = 0; i < MAXNUMP; ++i){
        // 如果记录的pid==0，表示空记录，continue
        if(shm[i].pid == 0){
            continue;
        }

        // 如果记录的pid!=0，表示是服务程序的心跳记录
        logfile.Write("i = %d, pid = %d, pname = %s, timeout = %d, atime = %d\n", i, 
                    shm[i].pid, shm[i].pname, shm[i].timeout, shm[i].atime);
        
        // 向进程发送信号0，判断它是否还存在，如果不存在，从共享内存中删除该记录，continue
        int iret = kill(shm[i].pid, 0);     // 进程存在kill()返回0，进程不存在返回-1
        if(iret == -1){
            logfile.Write("process pid = %d(%s) do not exist.\n", (shm+i)->pid, (shm+i)->pname);
            memset(shm+i, 0, sizeof(struct st_procinfo));
            continue;
        }

        time_t now = time(0);
        // 如果进程未超时，continue;
        if(now - shm[i].atime < shm[i].timeout){
            continue;
        }
        // 如果已超时，发送信号15，尝试正常终止进程
        logfile.Write("process pid = %d(%s) already timeout.\n", (shm+i)->pid, (shm+i)->pname);
        kill(shm[i].pid, 15);

        // 每隔1秒判断一次进程是否存在，累计5秒，一般来说，5秒的时间足够让进程退出
        for(int j = 0; j < 5; ++j){
            sleep(1);
            iret = kill(shm[i].pid, 0);
            if(iret == -1){         // 进程已退出
                break;
            }
        }
        // 如果进程仍存在，就发送信号9，强制终止它
        if(iret == -1){
            logfile.Write("process pid = %d(%s) already stopped.\n", (shm+i)->pid, (shm+i)->pname);
        }
        else{
            kill(shm[i].pid, 9);
            logfile.Write("process pid = %d(%s) have been forced stop.\n", (shm+i)->pid, (shm+i)->pname);
        }
        // 从共享内存中删除已超时进程的心跳记录
        memset(shm+i, 0, sizeof(struct st_procinfo));
    }
    
    // 把共享内存从当前进程中分离
    shmdt(shm);
    return 0;
}